package com.demo.spring.formework.context;

import com.demo.spring.formework.annotation.QBAutowired;
import com.demo.spring.formework.annotation.QBController;
import com.demo.spring.formework.annotation.QBService;
import com.demo.spring.formework.aop.*;
import com.demo.spring.formework.bean.QBBeanWrapper;
import com.demo.spring.formework.bean.config.QBBeanDefinition;
import com.demo.spring.formework.bean.config.QBBeanPostProcessor;
import com.demo.spring.formework.bean.support.QBBeanDefinitionReader;
import com.demo.spring.formework.bean.support.QBDefaultListableBeanFactory;
import com.demo.spring.formework.core.QBBeanFaction;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Data
public class QBApplicationContext extends QBDefaultListableBeanFactory implements QBBeanFaction {

    private String [] configLocations;
    private QBBeanDefinitionReader reader;

    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String,Object>();
    private Map<String, QBBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, QBBeanWrapper>();

    public QBApplicationContext(String ... configLocations){
        this.configLocations = configLocations;

        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void refresh() throws Exception {
        //1定位配置文件
        reader = new QBBeanDefinitionReader(this.configLocations);
        //加载配置文件，扫描相关类，封装成BeanDefinition
        List<QBBeanDefinition> beanDefinitionList = reader.loadBeanDefinitions();
        //注册，把配置信息放入容器中 伪容器
        doRegisterBeanDefinition(beanDefinitionList);
        //把不是延时加载的类，初始化
        doAutowried();

    }

    private void doAutowried() {
        for (Map.Entry<String, QBBeanDefinition> stringQBBeanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            String beanName = stringQBBeanDefinitionEntry.getKey();
            if(!stringQBBeanDefinitionEntry.getValue().isLazyInit()){
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void doRegisterBeanDefinition(List<QBBeanDefinition> beanDefinitionList) throws Exception{
        for (QBBeanDefinition beanDefinition : beanDefinitionList) {
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            super.beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
        }

    }


    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(beanClass.getName());
    }

    //依赖注入，从这里开始，通过读取BeanDefinition中的信息
    //然后，通过反射机制创建一个实例并返回
    //Spring做法是，不会把最原始的对象放出去，会用一个BeanWrapper来进行一次包装
    //装饰器模式：
    //1、保留原来的OOP关系
    //2、我需要对它进行扩展，增强（为了以后AOP打基础）
    public Object getBean(String beanName) throws Exception {

        QBBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

        //工厂模式 + 策略模式
        QBBeanPostProcessor postProcessor = new QBBeanPostProcessor();
        postProcessor.postProcessBeforeInitialization(null,beanName);

        //初始化
        Object instance = instantiateBean(beanName,beanDefinition);

        //3、把这个对象封装到BeanWrapper中
        QBBeanWrapper beanWrapper = new QBBeanWrapper(instance);
        this.factoryBeanInstanceCache.put(beanName,beanWrapper);

        postProcessor.postProcessAfterInitialization(instance,beanName);

        //3、注入到真正的容器中
        populateBean(beanName,new QBBeanDefinition(),beanWrapper);

        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();

    }

    private void populateBean(String beanName, QBBeanDefinition beanDefinition, QBBeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();

//        gpBeanDefinition.getBeanClassName();

        Class<?> clazz = (Class<?>)beanWrapper.getWrappedClass();
        //判断只有加了注解的类，才执行依赖注入
        if(!(clazz.isAnnotationPresent(QBController.class) || clazz.isAnnotationPresent(QBService.class))){
            return;
        }

        //获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if(!field.isAnnotationPresent(QBAutowired.class)){ continue;}

            QBAutowired autowired = field.getAnnotation(QBAutowired.class);

            String autowiredBeanName =  autowired.value().trim();
            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }

            //强制访问
            field.setAccessible(true);

            try {
                //为什么会为NULL，先留个坑
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){ continue; }
//                if(instance == null){
//                    continue;
//                }
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }

    private Object instantiateBean(String beanName, QBBeanDefinition beanDefinition) {
        //1、拿到要实例化的对象的类名
        String className = beanDefinition.getBeanClassName();

        //2、反射实例化，得到一个对象
        Object instance = null;
        try {
            //  gpBeanDefinition.getFactoryBeanName()
            //假设默认就是单例,细节暂且不考虑，先把主线拉通
            if(this.factoryBeanObjectCache.containsKey(className)){
                instance = this.factoryBeanObjectCache.get(className);
            }else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();

                QBAdvisedSupport config = instantionAopConfig(beanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);
                if(config.pointCutMatch()) {
                    instance = createProxy(config).getProxy();
                }

                this.factoryBeanObjectCache.put(className,instance);
                this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return instance;
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new
                String[this.beanDefinitionMap.size()]);
    }
    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }
    public Properties getConfig(){
        return this.reader.getConfig();
    }

    private QBAdvisedSupport instantionAopConfig(QBBeanDefinition beanDefinition) throws Exception{
        QBAopConfig config = new QBAopConfig();
        config.setPointCut(reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(reader.getConfig().getProperty("aspectAfterThrowingName"));
        config.setAspectAround(reader.getConfig().getProperty("aspectAround"));
        return new QBAdvisedSupport(config);
    }

    private QBAopProxy createProxy(QBAdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0) {
            return new QBJdkDynamicAopProxy(config);
        }
        return new QBCglibAopProxy(config);
    }


}
